{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# Jumping Kodee\n",
    "\n",
    "This notebook shows an animated Kodee sprite walking on the bottom of the JetBrains IDE window.\n",
    "\n",
    "It uses a custom Swing component that loads an animated GIF resource and renders it as an animation.\n",
    "\n",
    "Additionally, it demonstrates how to remove the animation by properly disposing of resources when the Kotlin Notebook kernel is restarted."
   ]
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "%use intellij-platform",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "import com.intellij.openapi.Disposable\n",
    "import com.intellij.openapi.util.Disposer\n",
    "import com.intellij.openapi.ui.DialogEarthquakeShaker\n",
    "import com.intellij.openapi.wm.WindowManager\n",
    "import java.awt.Dimension\n",
    "import java.awt.Graphics\n",
    "import java.net.URL\n",
    "import javax.swing.ImageIcon\n",
    "import javax.swing.JLabel\n",
    "import javax.swing.Timer\n",
    "import kotlin.math.PI\n",
    "import kotlin.math.cos\n",
    "\n",
    "class JumpingGifLabel(url: URL, onLand: () -> Unit) : JLabel(ImageIcon(url)), Disposable {\n",
    "    private var idx = 0\n",
    "    private val cycle = 17\n",
    "    private val ascent = 8.0\n",
    "    private val timer = Timer(40) {\n",
    "        parent ?: return@Timer\n",
    "        val (w, h) = with(preferredSize) { width to height }\n",
    "        val amp = (h / 2.0).coerceAtLeast(ascent)\n",
    "        val jump = when {\n",
    "            idx <= ascent -> amp * (1 - cos(PI * (idx / ascent))) / 2\n",
    "            else -> amp * (1 + cos(PI * ((idx - ascent) / (cycle - 1 - ascent)))) / 2\n",
    "        }\n",
    "\n",
    "        setBounds((parent.width - w) / 2, parent.height - h - jump.toInt(), w, h)\n",
    "\n",
    "        if (idx == cycle - 1) onLand()\n",
    "        idx = (idx + 1) % cycle\n",
    "        repaint()\n",
    "    }\n",
    "\n",
    "    init {\n",
    "        preferredSize = Dimension(icon.iconWidth, icon.iconHeight)\n",
    "    }\n",
    "\n",
    "    override fun addNotify() {\n",
    "        super.addNotify()\n",
    "        timer.start()\n",
    "    }\n",
    "\n",
    "    override fun removeNotify() {\n",
    "        timer.stop()\n",
    "        super.removeNotify()\n",
    "    }\n",
    "\n",
    "    override fun paintComponent(g: Graphics) {\n",
    "        g.drawImage((icon as ImageIcon).image, 0, 0, width, height, this)\n",
    "    }\n",
    "\n",
    "    override fun dispose() = timer.stop()\n",
    "}\n",
    "\n",
    "runInEdt {\n",
    "    val frame = WindowManager.getInstance()\n",
    "        .getFrame(currentProject())\n",
    "        ?: error(\"IDE frame not found\")\n",
    "\n",
    "    val kodee = javaClass\n",
    "        .getResource(\"jumping_kodee/kodee-happy.gif\")\n",
    "        ?: error(\"Kodee not found\")\n",
    "\n",
    "    val label = JumpingGifLabel(kodee) {\n",
    "        DialogEarthquakeShaker.shake(frame)\n",
    "    }\n",
    "\n",
    "    frame.layeredPane.add(label)\n",
    "    Disposer.register(notebookDisposable) {\n",
    "        frame.layeredPane.remove(label)\n",
    "        label.dispose()\n",
    "    }\n",
    "}\n"
   ],
   "outputs": [],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Kotlin",
   "language": "kotlin",
   "name": "kotlin"
  },
  "language_info": {
   "name": "kotlin",
   "version": "2.2.20-dev-7701",
   "mimetype": "text/x-kotlin",
   "file_extension": ".kt",
   "pygments_lexer": "kotlin",
   "codemirror_mode": "text/x-kotlin",
   "nbconvert_exporter": ""
  },
  "ktnbPluginMetadata": {
   "sessionRunMode": "IDE_PROCESS"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
